uiautomation 源码研究
2025/10/20
uiautomation 源码研究
- github https://github.com/leexgone/uiautomation-rs/
 - cargo doc https://docs.rs/uiautomation/0.24.0/uiautomation/
 
结构
项目结构
(略,先不管源码。先看 public api 的结构)
Features
(略,见 README)
包 (Re-exports)
pub use self::errors::Error;
pub use self::errors::Result;
pub use self::core::UIAutomation;
pub use self::core::UIElement;
pub use self::core::UITreeWalker;
pub use self::core::UIMatcher;模块 (modules)
Demo
github 有个 samples 文件夹,非常不错。这部分详见 ./samples
主要/常用 的 类/模块
见后面章节,后续一个章节一个类/模块
UIAutomation
原型:
// src/core.rs#83
impl UIAutomation {
    pub fn new() -> Result<UIAutomation>
    
    // get
    pub fn get_focused_element(&self) -> Result<UIElement>
    pub fn get_focused_element_build_cache(&self, cache_request: &UICacheRequest) -> Result<UIElement>
    pub fn get_root_element(&self) -> Result<UIElement>
    pub fn get_root_element_build_cache(&self, cache_request: &UICacheRequest) -> Result<UIElement>
    pub fn get_raw_view_walker(&self) -> Result<UITreeWalker>
    pub fn get_control_view_condition(&self) -> Result<UICondition>
    pub fn get_control_view_walker(&self) -> Result<UITreeWalker>
    pub fn get_content_view_condition(&self) -> Result<UICondition>
    pub fn get_content_view_walker(&self) -> Result<UITreeWalker>应用:
fn start_uia_worker(rx: Receiver<UiaMsg>) {
    thread::spawn(move || {
        // 初始化 uiautomation
        let automation = UIAutomation::new().unwrap();
        let walker = automation.get_control_view_walker().unwrap();
        loop {
            match rx.recv() {
                Ok(UiaMsg::PrintElement) => {
                    let _ = get_uia_focused(&walker, &automation, 0);
                }
                Err(_) => break,
            }
        }
    });
}UIElement
原型:
// src/core.rs#449
impl UIElement {
    find_xxx()
    // ...
    
    // get
    // 
    // 环境问题
    // VSCode 环境,控制台可能可能会让你按 Alt+Shift+F1, 开启后 VSCode 右下角会显示 "已为屏幕阅读器优化",这种情况下的 vscode 才能获取到信息
    // classname:   大部分是空 (浏览器 qq vscode 等空),notepad-- 是 ScintillaEditView,windows notepad 是 RichEditD2DPT
    // controltype: 大部分是Edit,windows notepad 是Document,浏览器搜索框是 ComboBox
    // name:        窗口名/输入框默认名
    // 
    // cache问题:
    // - **实时 vs. 缓存**:`CurrentXXX` 方法每次都实时访问底层数据,`CachedXXX` 方法只返回初始化或上次缓存时的数据。
    // - **性能**:缓存方法更快,但有可能不是最新的数据;实时方法更慢,但保证拿到当前状态。
    // - **使用场景**:如果只做一次性批量查询,用缓存更高效;如果要跟踪UI的变化,必须用实时方法。
    pub fn get_cached_parent(&self) -> Result<UIElement>
    pub fn get_cached_children(&self) -> Result<Vec<UIElement>>
    pub fn get_runtime_id(&self) -> Result<Vec<i32>>
    pub fn get_name(&self) -> Result<String> // 
    pub fn get_cached_name(&self) -> Result<String>
    pub fn get_automation_id(&self) -> Result<String>
    pub fn get_cached_automation_id(&self) -> Result<String>
    pub fn get_process_id(&self) -> Result<u32>
    pub fn get_cached_process_id(&self) -> Result<i32>
    pub fn get_classname(&self) -> Result<String> // 
    pub fn get_cached_classname(&self) -> Result<String>
    pub fn get_control_type(&self) -> Result<ControlType> // 
    pub fn get_cached_control_type(&self) -> Result<ControlType>
    pub fn get_localized_control_type(&self) -> Result<String>
    pub fn get_cached_localized_control_type(&self) -> Result<String>
    pub fn get_accelerator_key(&self) -> Result<String>
    pub fn get_cached_accelerator_key(&self) -> Result<String>
    pub fn get_access_key(&self) -> Result<String>
    pub fn get_cached_access_key(&self) -> Result<String>
    
    // is/has
    pub fn has_keyboard_focus(&self) -> Result<bool>
    pub fn has_cached_keyboard_focus(&self) -> Result<bool>
    pub fn is_keyboard_focusable(&self) -> Result<bool>
    pub fn is_cached_keyboard_focusable(&self) -> Result<bool>
    pub fn is_enabled(&self) -> Result<bool>
    pub fn is_cached_enabled(&self) -> Result<bool>
    
    pub fn get_help_text(&self) -> Result<String>
    pub fn get_cached_help_text(&self) -> Result<String>
    pub fn get_culture(&self) -> Result<i32>
    pub fn get_cached_culture(&self) -> Result<i32>
    pub fn is_control_element(&self) -> Result<bool>
    pub fn is_cached_control_element(&self) -> Result<bool>
    pub fn is_content_element(&self) -> Result<bool>
    pub fn is_cached_content_element(&self) -> Result<bool>
    pub fn is_password(&self) -> Result<bool>
    pub fn is_cached_password(&self) -> Result<bool>
    pub fn get_native_window_handle(&self) -> Result<Handle>
    pub fn get_cached_native_window_handle(&self) -> Result<Handle>
    pub fn get_item_type(&self) -> Result<String>
    pub fn get_cached_item_type(&self) -> Result<String>
    pub fn is_offscreen(&self) -> Result<bool>
    pub fn is_cached_offscreen(&self) -> Result<bool>
    pub fn get_orientation(&self) -> Result<OrientationType>
    pub fn get_cached_orientation(&self) -> Result<OrientationType>
    pub fn get_framework_id(&self) -> Result<String>
    pub fn get_cached_framework_id(&self) -> Result<String>
    pub fn is_required_for_form(&self) -> Result<bool>
    pub fn is_cached_required_for_form(&self) -> Result<bool>
    pub fn is_data_valid_for_form(&self) -> Result<bool>
    pub fn is_cached_data_valid_for_form(&self) -> Result<bool>
    pub fn get_item_status(&self) -> Result<String>
    pub fn get_cached_item_status(&self) -> Result<String>
    pub fn get_bounding_rectangle(&self) -> Result<Rect>
    pub fn get_cached_bounding_rectangle(&self) -> Result<Rect>
    pub fn get_labeled_by(&self) -> Result<UIElement>
    pub fn get_cached_labeled_by(&self) -> Result<UIElement>
    pub fn get_controller_for(&self) -> Result<Vec<UIElement>>
    pub fn get_cached_controller_for(&self) -> Result<Vec<UIElement>>
    pub fn get_described_by(&self) -> Result<Vec<UIElement>>
    pub fn get_cached_described_by(&self) -> Result<Vec<UIElement>>
    pub fn get_flows_to(&self) -> Result<Vec<UIElement>>
    pub fn get_cached_flows_to(&self) -> Result<Vec<UIElement>>
    pub fn get_provider_description(&self) -> Result<String>
    pub fn get_cached_provider_description(&self) -> Result<String>
    
    #[cfg(feature = "pattern")]
    pub fn get_pattern<T: super::patterns::UIPattern + TryFrom<IUnknown, Error = Error>>(&self) -> Result<T> // 
    #[cfg(feature = "pattern")]
    pub fn get_cached_pattern<T: super::patterns::UIPattern + TryFrom<IUnknown, Error = Error>>(&self) -> Result<T>
    pub fn get_clickable_point(&self) -> Result<Option<Point>>
    pub fn get_property_value(&self, property: UIProperty) -> Result<Variant>
    pub fn get_cached_property_value(&self, property: UIProperty) -> Result<Variant>
    pub fn show_context_menu(&self) -> Result<()>
    pub fn get_heading_level(&self) -> Result<HeadingLevel>
    pub fn get_cached_heading_level(&self) -> Result<HeadingLevel>
    pub fn is_dialog(&self) -> Result<bool>
    pub fn is_cached_dialog(&self) -> Result<bool>
    pub(crate) fn to_elements(elements: IUIAutomationElementArray) -> Result<Vec<UIElement>>
    // input类
    #[cfg(feature = "input")]
    pub fn send_keys(&self, keys: &str, interval: u64) -> Result<()>
    #[cfg(feature = "input")]
    pub fn hold_send_keys(&self, holdkeys: &str, keys: &str, interval: u64) -> Result<()>
    #[cfg(feature = "input")]
    pub fn send_text(&self, text: &str, interval: u64) -> Result<()>
    #[cfg(all(feature = "input", feature = "clipboard"))]
    pub fn send_text_by_clipboard(&self, text: &str) -> Result<()>
    #[cfg(feature = "input")]
    pub fn click(&self) -> Result<()>
    #[cfg(feature = "input")]
    pub fn hold_click(&self, holdkeys: &str) -> Result<()>
    #[cfg(feature = "input")]
    pub fn double_click(&self) -> Result<()>
    #[cfg(feature = "input")]
    pub fn right_click(&self) -> Result<()>
    #[cfg(feature = "input")]
    pub fn drag_to(&self, target: &UIElement) -> Result<()>
    pub fn get_click_point(&self) -> Result<Point>
}Walker
walker.get_first_child 可以用来遍历ui树
UITextPattern
Text
uiautomation-rs/crates/uiautomation/src/actions.rs
有获取光标位置的api
链接到当前文件 0
没有文件链接到当前文件